package com.ruubypay.biz.mvc.workflow.service.impl;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.ruubypay.biz.mvc.workflow.model.request.ProcessDefined;
import com.ruubypay.biz.mvc.workflow.model.response.ProcessDefinedResponse;
import com.ruubypay.biz.mvc.workflow.service.ProcessService;
import com.ruubypay.biz.utils.file.DelAllFile;
import com.ruubypay.biz.utils.file.FileUpload;
import com.ruubypay.biz.utils.file.PathUtil;
import com.ruubypay.biz.utils.io.resource.ResourceConverter;
import com.ruubypay.web.common.page.DataGrid;
import com.ruubypay.web.common.utils.StringUtils;
import org.activiti.bpmn.converter.BpmnXMLConverter;
import org.activiti.bpmn.model.BpmnModel;
import org.activiti.editor.constants.ModelDataJsonConstants;
import org.activiti.editor.language.json.converter.BpmnJsonConverter;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.repository.*;
import org.bouncycastle.util.Strings;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamReader;
import java.io.*;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import java.util.zip.ZipInputStream;

/**
 * 流程接口业务实现类
 * @author chenhaiyang
 */
@Service("processServiceImpl")
public class ProcessServiceImpl implements ProcessService{

    /**
     * acitviti生成的文件的存放地址
     */
    private static final String ACTIVITI_FILE_PATH= "activiti-upload/activitiFile";
    public static String getFilePath(){
        return ACTIVITI_FILE_PATH;
    }
    /**
     * 图片的base64格式的base64头
     */
    private static final String BASE_64_HEADER="data:image/jpeg;base64,";
    /**
     * 从外部系统导入资源流程文件的流程标识
     */
    private static final String OUT_EXPORT="export_from_out_system";
    /**
     * 激活流程的操作标识
     */
    private static final int PROCESS_UNSUSPENDED=1;
    /**
     * 挂起流程的操作标识
     */
    private static final int PROCESS_SUSPENDED=2;


    private static Logger logger = LoggerFactory.getLogger(ProcessServiceImpl.class);

    @Resource
    private RepositoryService repositoryService;

    /**
     * 获取流程定义信息
     * @param paramMap 参数列表
     * @param pageNumber 页码
     * @param pageSize 每页显示的条数
     * @return 返回流程列表
     */
    @Override
    public DataGrid listDataProcesses(Map<String, Object> paramMap, Integer pageNumber, Integer pageSize) {
        ProcessDefinitionQuery processDefinitionQuery = repositoryService.createProcessDefinitionQuery()
                .orderByProcessDefinitionVersion().asc();

        String key = String.valueOf(paramMap.get("key"));
        if (StringUtils.isNotBlank(key)) {
            processDefinitionQuery.processDefinitionKeyLike(key);
        }

        List<ProcessDefinition> modelList = processDefinitionQuery.listPage((pageNumber-1)*pageSize,pageSize);

        //保存2个对象，1个流程定义，1个流程实例
        List<ProcessDefinedResponse> results =
                Optional.ofNullable(modelList)
                .map(List::stream)
                .orElseGet(Stream::empty)
                .map(this::buildProcessDefinedResponseByProcessDefinition)
                .collect(Collectors.toList());

        return new DataGrid(processDefinitionQuery.count(), results);

    }

    /**
     * @param idArray 流程id数组
     * @return 返回删除结果
     */
    @Override
    public int deleteProcessService(String idArray) {
        Arrays.asList(Strings.split(idArray,',')).forEach(
                deploymentId ->  repositoryService.deleteDeployment(deploymentId,true)
        );
        return 1;
    }

    /**
     * 流程的激活或者挂起 processDefined.isSuspended =1 激活流程 ，2 挂起流程
     * @param processDefined 流程定义信息
     */
    @Override
    public void suspendedOrNo(ProcessDefined processDefined) {
        if (processDefined.getIsSuspended()==PROCESS_UNSUSPENDED) {
            repositoryService.activateProcessDefinitionById(processDefined.getId(),true,null);
        }
        if (processDefined.getIsSuspended()==PROCESS_SUSPENDED){
            repositoryService.suspendProcessDefinitionById(processDefined.getId(),true,null);
        }
    }

    /**
     * 创建zip文件
     * @param deploymentId 部署id
     */
    @Override
    public void createzips(String deploymentId) {
        //生成先清空之前生成的文件
        DelAllFile.delFolder(PathUtil.getClasspath()+ACTIVITI_FILE_PATH);
        List<String> names = repositoryService.getDeploymentResourceNames(deploymentId);
        names.stream().
                filter(name->!name.contains("zip"))
                    .forEach(resourceName->{
                        try (
                                InputStream in = repositoryService.getResourceAsStream(deploymentId, resourceName)
                            ){
                            //把文件上传到文件目录里面
                            FileUpload.copyFile(in,PathUtil.getClasspath()+ACTIVITI_FILE_PATH+ File.separator,resourceName);
                        } catch (IOException e) {
                            logger.error(e.getMessage(),e);
                        }
                    });
    }

    /**
     * 将流程转换为模型
     * @param processDefined 流程定义VO对象
     * @throws Exception 异常
     */
    @Override
    public void addModel(ProcessDefined processDefined) throws Exception {

        ProcessDefinition processDefinition = repositoryService.
                createProcessDefinitionQuery().
                processDefinitionId(processDefined.getId()).
                singleResult();

        InputStream bpmnStream = repositoryService.getResourceAsStream(
                processDefinition.getDeploymentId(),
                processDefinition.getResourceName());

        XMLInputFactory xif = XMLInputFactory.newInstance();
        InputStreamReader in = new InputStreamReader(bpmnStream, "UTF-8");
        XMLStreamReader xtr = xif.createXMLStreamReader(in);
        BpmnModel bpmnModel = new BpmnXMLConverter().convertToBpmnModel(xtr);

        ObjectNode modelNode = new BpmnJsonConverter().convertToJson(bpmnModel);
        Model modelData = repositoryService.newModel();
        modelData.setKey(processDefinition.getKey());
        modelData.setName(processDefinition.getName());
        modelData.setCategory("默认分类");
        modelData.setDeploymentId(processDefinition.getDeploymentId());
        modelData.setVersion(Integer.parseInt(String.valueOf(repositoryService.createModelQuery().
                modelKey(modelData.getKey()).count()+1)));

        ObjectNode modelObjectNode = new ObjectMapper().createObjectNode();
        modelObjectNode.put(ModelDataJsonConstants.MODEL_NAME, processDefinition.getName());
        modelObjectNode.put(ModelDataJsonConstants.MODEL_REVISION, modelData.getVersion());
        modelObjectNode.put(ModelDataJsonConstants.MODEL_DESCRIPTION, processDefinition.getDescription());
        modelData.setMetaInfo(modelObjectNode.toString());

        repositoryService.saveModel(modelData);
        repositoryService.addModelEditorSource(modelData.getId(),
                modelNode.toString().getBytes("utf-8"));

    }

    /**
     * 预览xml文件
     * @param deploymentId 部署id
     * @return 返回xml字符串代码
     * @throws IOException IO异常
     */
    @Override
    public String viewXmlFromDeploymentId(String deploymentId) throws IOException {

        ProcessDefinition processDefinition = repositoryService
                .createProcessDefinitionQuery()
                .deploymentId(deploymentId)
                .singleResult();

        InputStream resourceAsStream = repositoryService.getResourceAsStream(deploymentId,
                processDefinition.getResourceName());
        return ResourceConverter.converterInputStreamAsString(resourceAsStream);

    }

    /**
     * 获取png资源图片
     * @param deploymentId 部署id
     * @return 返回资源图片
     * @throws IOException IO异常
     */
    @Override
    public String viewPngFromDeploymentId(String deploymentId) throws IOException {
        ProcessDefinition processDefinition = repositoryService
                .createProcessDefinitionQuery()
                .deploymentId(deploymentId)
                .singleResult();
        InputStream resourceAsStream = repositoryService.getResourceAsStream(deploymentId,
                processDefinition.getDiagramResourceName());
        return BASE_64_HEADER+ResourceConverter.converterstreamAsBase64Str(resourceAsStream);

    }

    /**
     * 上传资源文件
     * @param inputStream 输入流
     * @throws IOException IO异常
     */
    @Override
    public void uploadZips(InputStream inputStream) throws IOException {

        try(
                ZipInputStream zipInputStream=new ZipInputStream(inputStream)
           ){
            DeploymentBuilder deploymentBuilder = repositoryService.createDeployment();
            deploymentBuilder.name(OUT_EXPORT);
            deploymentBuilder.addZipInputStream(zipInputStream);
            deploymentBuilder.deploy();
        }finally {
            inputStream.close();
        }
    }


    /**
     * 根据流程定义对象构建流程定义查询响应VO类
     * @param defnition 流程定义信息
     * @return 返回 ProcessDefinedResponse
     */
    private ProcessDefinedResponse buildProcessDefinedResponseByProcessDefinition(ProcessDefinition defnition){
        ProcessDefinedResponse processDefinedResponse = new ProcessDefinedResponse();

        processDefinedResponse.setId(defnition.getId());
        processDefinedResponse.setName(defnition.getName());
        processDefinedResponse.setCategory(defnition.getCategory());
        processDefinedResponse.setDeploymentId(defnition.getDeploymentId());
        processDefinedResponse.setKey(defnition.getKey());
        processDefinedResponse.setVersion(defnition.getVersion());
        processDefinedResponse.setResourceName(defnition.getResourceName());
        processDefinedResponse.setDiagramResourceName(defnition.getDiagramResourceName());
        processDefinedResponse.setSuspended(defnition.isSuspended());
        processDefinedResponse.setTenantId(defnition.getTenantId());

        String deploymentId = defnition.getDeploymentId();
        Deployment deployment = repositoryService.createDeploymentQuery().deploymentId(deploymentId).singleResult();
        processDefinedResponse.setDeploymentTime(deployment.getDeploymentTime());

        return processDefinedResponse;
    }

}
